A common need for 3D programs is to turn on or off various modes based on user input. Pop-up menus provide a simple mechanism for this type of input.
GLUT provides an easy-to-use API for cascading pop-up menus. Menus can be created, changed, and ``attached'' to a mouse button within a window. If a menu is attached to a button in a window, pressing the button will trigger the pop-up menu. If a menu entry is selected, the callback function for the menu is called. The callback is passed the associated value for the selected menu entry. Here's an example:
glutCreateMenu(choice_selected); glutAddMenuEntry("Enable lighting", 1); glutAddMenuEntry("Disable lighting", 2); glutAttachMenu(GLUT_RIGHT_BUTTON);
The result is a menu with two options to enable or disable lighting. The menu is associated with the current window and will be triggered when the right mouse button is pressed within the window. Selecting a menu item will call the choice_selected callback that might look like:
void choice_selected(int value) { if(value == 1) glEnable(GL_LIGHTING); if(value == 2) glDisable(GL_LIGHTING); glutPostRedisplay(); }
Notice that instead of naively redrawing the window with lighting
appropriately enabled or disabled, glutPostRedisplay is called.
The advantage of ``posting a redisplay'' instead of performing
the redraw explicitly is that removing the pop-up menu is likely
to damage the current window.
A ``posted redisplay'' can be potentially
combined with any pending Expose events
caused by unmapping the pop-up menu.
Multiple calls to glutPostRedisplay and any
pending Expose events will be combined if possible to minimize the
redisplays necessary.
Like windows, GLUT maintains a current menu and the glutCreateMenu routines returns an integer identifier for the menu being created. The glutSetMenu and glutGetMenu routines set and query the current menu. And menu callbacks implicitly set the current menu to the menu generating the callback. The routines glutAddMenuEntry and glutAttachMenu operate on the current menu.
Figure 3: An example of GLUT cascaded pop-up menus.
The menu identifier of a submenu is required for creating cascaded menus where one menu item can trigger the display of a submenu. Here's an example of creating a cascaded menu:
submenu = glutCreateMenu(polygon_mode); glutAddMenuEntry("Filled", 1); glutAddMenuEntry("Outline", 2); glutCreateMenu(main_menu); glutAddMenuEntry("Quit", 666); glutAddSubMenu("Polygon mode", submenu); glutAttachMenu(GLUT_RIGHT_BUTTON);Figure 3 shows what the above menu would look like. Menus can be cascaded arbitrarily deeply (but menu recursion is not permitted).
Routines exist to modify menu items in the current menu. The glutChangeToMenuEntry and glutChangeToSubMenu routines can be used to change an existing menu item in the current menu to an entry or submenu respectively. Menu items can also be deleted using glutRemoveMenuItem.
When menus are activated, the GLUT main loop continues to process events, handle timeouts, and call the idle function. Sometimes a program might want to suspend activity like the idle callback when a menu is in use. The glutMenuStateFunc can register a callback for this purpose, where the callback routine might look like:
void menu_status(int status) { if(status == GLUT_MENU_IN_USE) glutIdleFunc(NULL); else glutIdleFunc(animate); }
GLUT's menu support provides an easy way to let users see available options and select between various modes. In conjunction with the other input and window management support in the GLUT library, a programmer can quickly develop programs to explore OpenGL and 3D programming.